/*    file: HwndPresenter.cpp
 *    desc:
 * 
 * created: 2014-01-21 16:52:11
 *  author: zhengchuanjiang
 * company: 
 */

//#include "stdafx.h"
#include "HwndPresenter.h"
#include "DateTime.h"
#include "TStringUtil.h"


HwndPresenter::HwndPresenter():
m_context(),
m_picture(),
m_maxWidth(),
m_maxHeight(),
m_bmpInfo(),
m_showInfo(),
m_font()
{
    memset(&m_picture, 0, sizeof(m_picture));

    memset(&m_bmpInfo, 0, sizeof(m_bmpInfo));
    m_bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    m_bmpInfo.bmiHeader.biCompression = BI_RGB;
    m_bmpInfo.bmiHeader.biBitCount = 32;
    m_bmpInfo.bmiHeader.biPlanes = 1;

    m_font = CreateFont(100, 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
        CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_MODERN, TEXT("Times New Roman"));
}

HwndPresenter::~HwndPresenter()
{
    reset();

    if (m_font)
    {
        DeleteObject(m_font);
        m_font = NULL;
    }
}

void HwndPresenter::reset()
{
    m_context.reset();

    avpicture_free(&m_picture);

    memset(&m_picture, 0, sizeof(m_picture));
    m_maxWidth = 0;
    m_maxHeight = 0;
}

bool HwndPresenter::draw(HWND hwnd, AVFrame* pFrame, int ratioX, int ratioY)
{
	if (!pFrame || (pFrame->width <= 0))
	{
		return false;
	}

    RECT rcWin;
    BOOL ret = ::GetClientRect(hwnd, &rcWin);
    if (ret == FALSE)
    {
        return false;
    }

    if (IsRectEmpty(&rcWin))
    {
        return false;
    }

    bool success = false;
    RECT rcDest = getDestRect(pFrame->width, pFrame->height,
        rcWin.right - rcWin.left, rcWin.bottom - rcWin.top, ratioX, ratioY);
    if (IsRectEmpty(&rcDest))
    {
        return false;
    }

    int width = rcDest.right - rcDest.left;
    int height = rcDest.bottom - rcDest.top;
    if (m_context.update(pFrame, width, height))
    {
        resetPicture(width, height);
    }

    sws_scale(m_context.m_pContext, pFrame->data, pFrame->linesize, 0, pFrame->height,
        m_picture.data, m_picture.linesize);

    drawPicture(hwnd, rcDest, m_picture, pFrame);

    return success;
}

bool HwndPresenter::draw(HWND hwnd, AVFrame* pFrame)
{
    return draw(hwnd, pFrame, 0, 0);
}

bool HwndPresenter::drawCache(HWND hwnd, int ratioX, int ratioY)
{
    RECT rcWin;
    BOOL ret = ::GetClientRect(hwnd, &rcWin);
    if (ret == FALSE)
    {
        return false;
    }

    if (IsRectEmpty(&rcWin))
    {
        return false;
    }

    RECT rcDest = getDestRect(m_context.m_outWidth, m_context.m_outHeight,
        rcWin.right - rcWin.left, rcWin.bottom - rcWin.top, ratioX, ratioY);
    if (IsRectEmpty(&rcDest))
    {
        return false;
    }

    //RECT rcSrc = {0, 0, m_context.m_outWidth, m_context.m_outHeight};

    AVPicture outPic;
    scalePicture(outPic, rcDest.right - rcDest.left, rcDest.bottom - rcDest.top,
            m_picture, m_context.m_outWidth, m_context.m_outHeight);

    drawPicture(hwnd, rcDest, outPic);

    return true;
}

void HwndPresenter::drawPicture(HWND hwnd, const RECT& rc, AVPicture& pic, AVFrame* frame)
{
    HDC hdc = ::GetDC(hwnd);

    m_bmpInfo.bmiHeader.biWidth = rc.right - rc.left;
    m_bmpInfo.bmiHeader.biHeight = - (rc.bottom - rc.top);

    SetDIBitsToDevice(hdc,
        rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
        0, 0, 0, rc.bottom - rc.top,
        pic.data[0], &m_bmpInfo, 0);

    if (frame)
    {
        if (m_showInfo)
        {
            HFONT oldFont = (HFONT)SelectObject(hdc, m_font);
            SetTextColor(hdc, RGB(0,0, 255));

            char text[MAX_PATH];
            sprintf_s(text, sizeof(text), "%d*%d", frame->width, frame->height);

            comn::DateTime dt = comn::DateTime::now();
            std::string strNum = comn::StringUtil::format("%02d:%03d", dt.getSecond(), dt.getMillisecond());
            TextOut(hdc, 20, 20, strNum.c_str(), strNum.size());

            SelectObject(hdc, oldFont);
        }

        RECT rcText(rc);
        //DrawText(hdc, text, strlen(text), &rcText, DT_LEFT);

        if (m_osd.size() > 0)
        {
            rcText.top += 20;
            DrawText(hdc, m_osd.c_str(), m_osd.size(), &rcText, DT_LEFT);
        }
        
    }

    ::ReleaseDC(hwnd, hdc);
}

void HwndPresenter::drawPicture(HWND hwnd, const RECT& rc, AVPicture& pic)
{
    HDC hdc = ::GetDC(hwnd);

    m_bmpInfo.bmiHeader.biWidth = rc.right - rc.left;
    m_bmpInfo.bmiHeader.biHeight = - (rc.bottom - rc.top);

    SetDIBitsToDevice(hdc,
            rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
            0, 0, 0, rc.bottom - rc.top,
            pic.data[0], &m_bmpInfo, 0);
    
    ::ReleaseDC(hwnd, hdc);
}

RECT HwndPresenter::getDestRect(int picWidth, int picHeight, 
                                int destWidth, int destHeight,
                                    int ratioX, int ratioY)
{
    RECT rc = {0, 0, destWidth, destHeight};
    if ((ratioX == 0) && (ratioY == 0))
    {
        // raw ratio
        rc = getScaleRect(rc, picWidth, picHeight);
    }
    else if ((ratioX < 0) || (ratioY < 0))
    {
        // fit window
    }
    else
    {
        rc = getScaleRect(rc, ratioX, ratioY);
    }
    return rc;
}

RECT HwndPresenter::getScaleRect(const RECT& rc, int ratioX, int ratioY)
{
    RECT rcOut = rc;
    int rcWidth = rc.right - rc.left;
    int rcHeight = rc.bottom - rc.top;
    int diff = rcWidth * ratioY - rcHeight * ratioX;
    if (diff > 0)
    {
        rcOut.top = rc.top;
        rcOut.bottom = rc.bottom;
        int width = rcHeight * ratioX / ratioY;
        rcOut.left = rc.left + (rcWidth - width)/2;
        rcOut.right = rcOut.left + width;
    }
    else if (diff < 0)
    {
        rcOut.left = rc.left;
        rcOut.right = rc.right;
        int height = rcWidth * ratioY / ratioX;
        rcOut.top = rc.top + (rcHeight - height)/2;
        rcOut.bottom = rcOut.top + height;
    }
    return rcOut;   
}

void HwndPresenter::resetPicture(int width, int height)
{
    if ((width > m_maxWidth) || (height > m_maxHeight))
    {
        avpicture_free(&m_picture);

        avpicture_alloc(&m_picture, AV_PIX_FMT_RGB32, width, height);
        int size = avpicture_get_size(AV_PIX_FMT_RGB32, width, height);
        memset(m_picture.data[0], 0x80, size);

        m_maxWidth = width;
        m_maxHeight = height;
    }
    else
    {
        m_picture.linesize[0] = width * 4;
    }
}

bool HwndPresenter::scalePicture(AVPicture& outPic, int outWidth, int outHeight,
                  AVPicture& srcPic, int srcWidth, int srcHeight)
{
    bool done = false;
    avpicture_alloc(&outPic, AV_PIX_FMT_RGB32, outWidth, outHeight);

    SwsContext* pContext = sws_getContext(srcWidth, srcHeight, AV_PIX_FMT_RGB32,
        outWidth, outHeight, AV_PIX_FMT_RGB32, SWS_FAST_BILINEAR, 0, 0, 0);
    if (pContext)
    {
        int ret = sws_scale(pContext, srcPic.data, srcPic.linesize, 0, srcHeight,
            outPic.data, outPic.linesize);

        sws_freeContext(pContext);

        done = (ret > 0);
    }

    return done;
}

void HwndPresenter::setOsd(const std::string& text)
{
    m_osd = text;
}

void HwndPresenter::enableShowFrameInfo(bool enabled)
{
    m_showInfo = enabled;
}
